package com.citycloud.ccuap.tc.admin.sec.secuser;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import com.citycloud.ccuap.commons.application.Application;
import com.citycloud.ccuap.commons.sys.entity.SysRole;
import com.citycloud.ccuap.commons.sys.repository.SysGroupRepository;
import com.citycloud.ccuap.commons.sys.repository.SysRoleRepository;
import com.citycloud.ccuap.commons.sys.repository.SysUserRepository;
import com.citycloud.ccuap.tc.admin.datagroup.DataGroupMapper;
import com.citycloud.ccuap.tc.admin.entity.SysDatagroup;
import com.citycloud.ccuap.tc.admin.repository.SysDatagroupRepository;
import com.citycloud.ccuap.tc.admin.repository.SysProjectFunctionRepository;
import com.citycloud.ccuap.tc.yq.constant.YqConstant;
import com.citycloud.ccuap.tc.yq.entity.MetaField;
import com.citycloud.ccuap.tc.yq.entity.SysDimensionBustype;
import com.citycloud.ccuap.tc.yq.entity.SysDimensionTableConfig;
import com.citycloud.ccuap.tc.yq.entity.SysResultSymbol;
import com.citycloud.ccuap.tc.yq.metafield.MetaFieldMapper;
import com.citycloud.ccuap.tc.yq.repository.MetaFieldRepository;
import com.citycloud.ccuap.tc.yq.repository.SysDimensionBustypeRepository;
import com.citycloud.ccuap.tc.yq.repository.SysDimensionTableConfigRepository;
import com.citycloud.ccuap.tc.yq.view.FieldWhere;
import com.citycloud.ccuap.tc.yq.view.Operator;
import com.citycloud.ccuap.tc.yq.view.TableField;

/***
 * @className SecUserCacheService
 * @author wangyj
 * @Date 2019/6/21 14:43
 * @description
 */
@Service
public class SecUserCacheService implements ISecUserCacheService {
    
    private static Log logger = LogFactory.getLog(SecUserCacheService.class);
    
    public static String CUSTOMS_CODE_0000 = "0000";
    
    public static String CUSTOMS_CODE_00 = "00";
    
    public static String SAME_ORG = "ccuap_org";
    
    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    private SysUserRepository sysUserRepository;
    
    @Autowired
    private SysGroupRepository syGroupRepository;
    
    @Autowired
    SysProjectFunctionRepository sysProjectFunctionRepository;
    
    @Autowired
    SysDatagroupRepository sysDatagroupRepository;
    
    @Autowired
    SysDimensionTableConfigRepository sysDimensionTableConfigRepository;
    
    @Autowired
    SysDimensionBustypeRepository sysDimensionBustypeRepository;
    
    @Autowired
    SysRoleRepository sysRoleRepository;
    
    @Autowired
    MetaFieldRepository metaFieldRepository;

    @Autowired
    @Lazy
    MetaFieldMapper metaFieldMapper;
    
    @Autowired
    @Lazy
    SecUserMapper secUserMapper;

    @Autowired
    @Lazy
    DataGroupMapper dataGroupMapper;
    
    @Override
    public void buildAllDataGroupAndRoleCache() {
        //获取开始时间
        long startTime = System.currentTimeMillis();
        logger.debug("----------------------开始构建缓存----------------------");
        buildAllDataGroupCache();
        buildAllRoleDataGroupCache();
        //获取结束时间
        long endTime = System.currentTimeMillis();
        //平台启动后，如果要实现初始化操作可以在此实现
        logger.debug("----------------------构建缓存结束----------------------");
        logger.debug("----------------------构建时间：" + (endTime - startTime) / 1000 + "s----------------------");
        
        
    }
    
    @Override
    public void deleteRoleDataGroupCache(Long roleId){
        if (redisTemplate.opsForHash().hasKey(YqConstant.CACHE_KEY_DATAGROUP_ROLE, String.valueOf(roleId))){
            redisTemplate.opsForHash().delete(YqConstant.CACHE_KEY_DATAGROUP_ROLE, String.valueOf(roleId));
        }
        
    }
    
    @Override
    public void deleteDataGroupCacheById(String datagroupId){
        Set<String> keys = redisTemplate.opsForHash().keys(YqConstant.CACHE_KEY_DATAGROUP + ":" + datagroupId);
        if(CollectionUtils.isNotEmpty(keys)){
            keys.stream().forEach(key ->{
                redisTemplate.opsForHash().delete(YqConstant.CACHE_KEY_DATAGROUP + ":" +datagroupId ,key);
            });
        }
    }
    
    @Override
    public void buildAllDataGroupCache() {
        
        Set<SysDatagroup> datagroups = sysDatagroupRepository.findAllByIsDeletedFalse();
        for (SysDatagroup datagroup : datagroups) {
            buildDataGroupCacheById(datagroup.getDatagroupId());
        }
        
    }
    
    @Override
    public void buildDataGroupCacheById(String datagroupId) {
        deleteDataGroupCacheById(datagroupId);
        List<SysDimensionTableConfig> sysDimensionTableConfigs = sysDimensionTableConfigRepository.findByDatagroupId(datagroupId);
        buildDatagroupCache(sysDimensionTableConfigs);
        redisTemplate.opsForHash().put(YqConstant.CACHE_KEY_DATAGROUP + ":" + datagroupId, YqConstant.CACHE_KEY_DATAGROUP_CLSS, sysDimensionTableConfigs.stream().map(SysDimensionTableConfig::getTableId).collect(Collectors.toList()));
        sysDimensionTableConfigs.stream().collect(Collectors.groupingBy(SysDimensionTableConfig::getDsId)).forEach((key, value) -> {
            redisTemplate.opsForHash().put(YqConstant.CACHE_KEY_DATAGROUP + ":" + datagroupId, YqConstant.CACHE_KEY_DATAGROUP_CLS + ":" + key, value.stream().map(SysDimensionTableConfig::getTableId).collect(Collectors.toList()));
        });
        List<FieldWhere> fieldWheres = new ArrayList<FieldWhere>();
        Map<String, SysResultSymbol> resultSymbolMap = (Map<String, SysResultSymbol>) Application.getAttribute(YqConstant.RESULT_SYMBOL_KEY);
        List<SysDimensionBustype> sysDimensionBustypes = sysDimensionBustypeRepository.findByDatagroupId(datagroupId);
        for (SysDimensionBustype sysDimensionBustype : sysDimensionBustypes) {
            SysResultSymbol symbol;
            FieldWhere fieldWhere;
            String condition = null;
            String desc;
            switch (sysDimensionBustype.getSpecialType()) {
                // 当业务对象为组织且选择为“本机构”时
                case 1:
                    fieldWhere = new FieldWhere();
                    fieldWhere.setType("1");
                    fieldWhere.setBusiObjId(sysDimensionBustype.getBustypeObjId());
                    fieldWheres.add(fieldWhere);
                    break;
                // 当业务对象为组织且选择为“本机构及下级机构”时
                case 2:
                    fieldWhere = new FieldWhere();
                    fieldWhere.setType("2");
                    fieldWhere.setBusiObjId(sysDimensionBustype.getBustypeObjId());
                    fieldWheres.add(fieldWhere);
                    break;
                // 当业务对象为组织且选择为“其他机构及下级机构”时
                case 3:
                    desc = sysDimensionBustype.getBustypeDescbak();
                    fieldWhere = new FieldWhere();
                    fieldWhere.setType("3");
                    fieldWhere.setBusiObjId(sysDimensionBustype.getBustypeObjId());
                    fieldWhere.setFieldCon(desc);
                    fieldWheres.add(fieldWhere);
                    break;
                // 默认选择的不是业务不是组织时,按常规处理
                default:
                    condition = sysDimensionBustype.getBustypeCondition();
                    desc = sysDimensionBustype.getBustypeDesc();
                    symbol = resultSymbolMap.get(condition);
                    
                    if (symbol.getSymbolCode().contains("LIKE")) {
                        desc = "%" + desc + "%";
                    }
                    fieldWhere = new FieldWhere(null, getOperator(symbol.getSymbolCode()), desc, datagroupId);
                    fieldWhere.setType("4");
                    fieldWhere.setBusiObjId(sysDimensionBustype.getBustypeObjId());
                    fieldWheres.add(fieldWhere);
                    break;
            }
        }
        
        redisTemplate.opsForHash().put(YqConstant.CACHE_KEY_DATAGROUP + ":" + datagroupId, YqConstant.CACHE_KEY_DATAGROUP_BUSI, fieldWheres);
        
    }
    
    @Override
    public void buildUserProjectRoleCache(Long projectId, Long userId) {
        List<Long> roleIds = secUserMapper.getRoleIdsByProjectIdAndUserId(projectId, userId);
        redisTemplate.opsForHash().put(YqConstant.CACHE_KEY_DATAGROUP_USER + ":" + projectId, String.valueOf(userId), roleIds);
    }
    
    @Override
    public void rebuildDatagroupCacheByTableId(String tableId){
        List<SysDimensionTableConfig> datagroups = sysDimensionTableConfigRepository.findAllByTableId(tableId);
        buildDatagroupCache(datagroups);
    }
    
    @Override
    public void buildDatagroupCache(List<SysDimensionTableConfig> sysDimensionTableConfigs) {
            List<String> enames;
            List<MetaField> metaFields;
            for (SysDimensionTableConfig sysDimensionTableConfig : sysDimensionTableConfigs) {
                if (StringUtils.isNotEmpty(sysDimensionTableConfig.getFieldEnames())) {
                    redisTemplate.opsForHash().put(YqConstant.CACHE_KEY_DATAGROUP + ":" + sysDimensionTableConfig.getDatagroupId(), YqConstant.CACHE_KEY_DATAGROUP_TABLE + ":" + sysDimensionTableConfig.getTableId(), sysDimensionTableConfig.getFieldEnames());
    
                    enames = Arrays.asList(sysDimensionTableConfig.getFieldEnames().split(","));
                    metaFields = metaFieldMapper.getFieldsByTableIdAndEnamesAndBusiTypeNotNull(sysDimensionTableConfig.getTableId());
                    Map<String, List<String>> map = new HashMap<String, List<String>>();
                    metaFields.forEach(metaField -> {
                        List<String> list = map.get(metaField.getBusType());
    
                        if (null == list) {
                            list = new ArrayList<String>();
                        }
                        list.add(metaField.getEname());
                        map.put(metaField.getBusType(), list);
                    });
                    redisTemplate.opsForHash().put(YqConstant.CACHE_KEY_DATAGROUP + ":" + sysDimensionTableConfig.getDatagroupId(), YqConstant.CACHE_KEY_DATAGROUP_BUSI + ":" + sysDimensionTableConfig.getTableId(), map);
                }
            }
    }
    @Override
    public void buildAllRoleDataGroupCache() {
        List<SysRole> sysRoles = secUserMapper.findUndeletedRoles();
        sysRoles.forEach(sysRole -> {
            buildRoleDataGroupCache(sysRole.getRoleId());
        });
    }
    
    @Override
    public void buildRolesDataGroupCacheByDataGroupId(String datagroupId) {
        List<SysRole> sysRoles = secUserMapper.findRolesByDataGroupId(datagroupId);
        sysRoles.forEach(sysRole -> {
            buildRoleDataGroupCache(sysRole.getRoleId());
        });
    }
    
    @Override
    public void buildRoleDataGroupCache(Long roleId) {
        deleteRoleDataGroupCache(roleId);
        List<String> datagroupIds = dataGroupMapper.findDatagroupIdsByRoleId(roleId);
        redisTemplate.opsForHash().put(YqConstant.CACHE_KEY_DATAGROUP_ROLE, String.valueOf(roleId), datagroupIds);
    }
    
    
    @Override
    public Set<String> getDataPermissionsOfTableByUser(Long userId, Long projectId, String datasource) {
        
        Set<String> dataGroupIds = getDataGroupIdsByProjectIdAndUserId(userId, projectId);
        Set<String> tablesIds = new HashSet<>();
        dataGroupIds.forEach(datagorup -> {
            Object datagroupObject = null;
            if (StringUtils.isNotBlank(datasource)) {
                datagroupObject = redisTemplate.opsForHash().get(YqConstant.CACHE_KEY_DATAGROUP + ":" + datagorup, YqConstant.CACHE_KEY_DATAGROUP_CLS + ":" + datasource);
            } else {
                datagroupObject = redisTemplate.opsForHash().get(YqConstant.CACHE_KEY_DATAGROUP + ":" + datagorup, YqConstant.CACHE_KEY_DATAGROUP_CLSS);
                
            }
            if (null != datagroupObject) {
                tablesIds.addAll((List<String>) datagroupObject);
            }
        });
        return tablesIds;
    }
    
    @Override
    public Set<String> getDataPermissionsOfFieldByUserAndTable(Long userId, Long projectId, String tableValue) {
        Set<String> fields = new HashSet<String>();
        Set<String> dataGroupIds = getDataGroupIdsByProjectIdAndUserId(userId, projectId);
        dataGroupIds.stream().forEach(datagroupId -> {
            Object fieldObject = redisTemplate.opsForHash().get(YqConstant.CACHE_KEY_DATAGROUP + ":" + datagroupId, YqConstant.CACHE_KEY_DATAGROUP_TABLE + ":" + tableValue);
            if (null != fieldObject && StringUtils.isNotBlank((String) fieldObject)) {
                fields.addAll(Arrays.asList(((String) fieldObject).split(",")));
            }
        });
        
        return fields;
        
    }
    
    private Set<String> getDataGroupIdsByProjectIdAndUserId(Long userId, Long projectId) {
        Object objectRoles = redisTemplate.opsForHash().get(YqConstant.CACHE_KEY_DATAGROUP_USER + ":" + projectId, String.valueOf(userId));
        List<Long> roles = new ArrayList<Long>();
        if (null != objectRoles) {
            roles = (List<Long>) objectRoles;
        }
        
        Set<String> dataGroupIds = new HashSet<String>();
        roles.forEach(role -> {
            Object datagroupId = redisTemplate.opsForHash().get(YqConstant.CACHE_KEY_DATAGROUP_ROLE, String.valueOf(role));
            if (null != datagroupId ) {
                dataGroupIds.addAll((List<String>) datagroupId);
            }
        });
        return dataGroupIds;
    }
    
    /**
     * 获取用户指定表下拥有权限的字段 以及字段条件
     */
    @Override
    public TableField getDataPermissionsOfTableField(Long userId, Long projectId, String tableValue, String groupNo) {
        TableField tableField = new TableField();
        Set<String> dataGroupIds = getDataGroupIdsByProjectIdAndUserId(userId, projectId);
        
        Set<String> fields = new HashSet<String>();
        List<FieldWhere> fildWheres = new ArrayList<FieldWhere>();
        List<FieldWhere> fildSpeciWheres = new ArrayList<>();
        List<String> specialWhereStrs = new ArrayList<String>();
        
        dataGroupIds.stream().forEach(datagroupId -> {
            Object fieldObject = redisTemplate.opsForHash().get(YqConstant.CACHE_KEY_DATAGROUP + ":" + datagroupId, YqConstant.CACHE_KEY_DATAGROUP_TABLE + ":" + tableValue);
            if (null != fieldObject && StringUtils.isNotBlank((String) fieldObject)) {
                fields.addAll(Arrays.asList(((String) fieldObject).split(",")));
            }
            
            Object busiObject = redisTemplate.opsForHash().get(YqConstant.CACHE_KEY_DATAGROUP + ":" + datagroupId, YqConstant.CACHE_KEY_DATAGROUP_BUSI);
            
            if (null != busiObject) {
                Object busiTableObject = redisTemplate.opsForHash().get(YqConstant.CACHE_KEY_DATAGROUP + ":" + datagroupId, YqConstant.CACHE_KEY_DATAGROUP_BUSI + ":" + tableValue);
                if (null != busiTableObject) {
                    List<FieldWhere> fieldWheresCache = (List<FieldWhere>) busiObject;
                    Map<String, List<String>> fieldMap = (Map<String, List<String>>) busiTableObject;
                    fieldWheresCache.forEach(fieldWhereCache -> {
                        List<String> fieldList = fieldMap.get(fieldWhereCache.getBusiObjId());
                        if (null != fieldList && fieldList.size() > 0) {
                            fieldList.forEach(f -> {
                                switch (fieldWhereCache.getType()) {
                                    case "1":
                                        if(!specialWhereStrs.contains(f+ Operator.EQUAL.getValue() + groupNo)){
                                            specialWhereStrs.add(f+ Operator.EQUAL.getValue() + groupNo);
                                            fildSpeciWheres.add(new FieldWhere(f, Operator.EQUAL, groupNo, SAME_ORG));
                                        }
                                        
                                        break;
                                    case "2":
                                        if(!specialWhereStrs.contains(f+ Operator.LIKE.getValue() + groupNo)){
                                            specialWhereStrs.add(f+ Operator.LIKE.getValue() + groupNo);
                                            fildSpeciWheres.add(new FieldWhere(f, Operator.LIKE, groupNo + "%", SAME_ORG));
                                        }
                                        break;
                                    case "3":
                                        String[] descArr = fieldWhereCache.getFieldCon().split(",");
                                        if (descArr != null && descArr.length > 0) {
                                            for (String descTemp : descArr) {
                                                if(!specialWhereStrs.contains(f+ Operator.LIKE.getValue() + descTemp)){
                                                    specialWhereStrs.add(f+ Operator.LIKE.getValue() + descTemp);
                                                    fildSpeciWheres.add(new FieldWhere(f, Operator.LIKE, descTemp + "%", SAME_ORG));
    
    
                                                }
                                            }
                                        }
                                        break;
                                    case "4":
                                        fildWheres.add(new FieldWhere(f, fieldWhereCache.getFieldOp(), fieldWhereCache.getFieldCon(), fieldWhereCache.getDataGroupId()));
                                        break;
                                    default:
                                }
                                
                            });
                            
                        }
                        
                    });
                }
            }
            
            
        });
    
        // 对设定结果做一次分组
        Map<String, List<FieldWhere>> fieldWhereGroup = fildWheres.stream().collect(Collectors.groupingBy(FieldWhere::getFieldName));
        List<FieldWhere>   fieldWheres = new ArrayList<FieldWhere>();
        for(Map.Entry<String, List<FieldWhere>> map : fieldWhereGroup.entrySet()){
            map.getValue().stream().forEach(f ->{
                f.setDataGroupId(map.getKey());
                fieldWheres.add(f);
            });
        }
        

        fieldWheres.addAll(fildSpeciWheres);
        tableField.setFieldWheres(fieldWheres);
        tableField.setShowFields(new ArrayList<>(fields));
        
        
        return tableField;
    }
    
    /**
     * 根据运算符获取运算符枚举类型 描述: <br>
     * 1、…<br>
     * 2、…<br>
     *
     * @param op
     * @return
     * @see
     */
    private Operator getOperator(String op) {
        Operator operator = null;
        op = op.toLowerCase();
        switch (op) {
            case ">=": {
                operator = Operator.GREATERTHANEQUAL;
                break;
            }
            case ">": {
                operator = Operator.GREATERTHAN;
                break;
            }
            case "<=": {
                operator = Operator.LESSTHANEQUAL;
                break;
            }
            case "<": {
                operator = Operator.LESSTHAN;
                break;
            }
            case "like": {
                operator = Operator.LIKE;
                break;
            }
            case "<>": {
                operator = Operator.NOTEQUAL;
                break;
            }
            case "in": {
                operator = Operator.IN;
                break;
            }
            case "notlike": {
                operator = Operator.NOTLIKE;
                break;
            }
            default: {
                operator = Operator.EQUAL;
            }
            
        }
        return operator;
    }
    
}